package cn.anoxia.springframework.aop.framework.autoproxy;

import cn.anoxia.springframework.aop.*;
import cn.anoxia.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import cn.anoxia.springframework.aop.framework.ProxyFactory;
import cn.anoxia.springframework.beans.BeanFactory;
import cn.anoxia.springframework.beans.BeansException;
import cn.anoxia.springframework.beans.PropertyValues;
import cn.anoxia.springframework.beans.factory.BeanFactoryAware;
import cn.anoxia.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import cn.anoxia.springframework.beans.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * The class DefaultAdvisorAutoProxyCreator.
 * <p>
 * Description:
 *
 * @author: huangle
 * @since: 2021/08/31 10:58
 */
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

  private DefaultListableBeanFactory beanFactory;

  private final Set<Object> earlyProxyReferences = Collections.synchronizedSet(new HashSet<Object>());

  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    this.beanFactory = (DefaultListableBeanFactory) beanFactory;
  }

  @Override
  public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
//    if (isInfrastructureClass(beanClass)) {
//      return null;
//    }
//    // 获取所有的拦截方法
//    Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
//    for (AspectJExpressionPointcutAdvisor advisor : advisors){
//      ClassFilter classFilter = advisor.getPointcut().getClassFilter();
//      if (!classFilter.matches(beanClass)) {
//        continue;
//      }
//      AdvisedSupport advisedSupport = new AdvisedSupport();
//
//      TargetSource targetSource = null;
//
//      try {
//        // 初始化一个源对象
//        // getDeclaredConstructor获取构造函数
//        targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
//      }catch (Exception e){
//        e.printStackTrace();
//      }
//
//      advisedSupport.setTargetSource(targetSource);
//      advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
//      advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
//      advisedSupport.setProxyTargetClass(false);
//
//      return new ProxyFactory(advisedSupport).getProxy();
//    }

    return null;
  }

  @Override
  public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
    return true;
  }

  @Override
  public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
    earlyProxyReferences.add(beanName);
    return wrapIfNecessary(bean,beanName);
  }

  protected Object wrapIfNecessary(Object bean, String beanName) throws BeansException {
    if (isInfrastructureClass(bean.getClass())) {
      return bean;
    }

    Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();

    for (AspectJExpressionPointcutAdvisor advisor : advisors) {
      ClassFilter classFilter = advisor.getPointcut().getClassFilter();
      // 过滤匹配类
      if (!classFilter.matches(bean.getClass())) continue;

      AdvisedSupport advisedSupport = new AdvisedSupport();

      TargetSource targetSource = new TargetSource(bean);
      advisedSupport.setTargetSource(targetSource);
      advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
      advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
      advisedSupport.setProxyTargetClass(true);

      // 返回代理对象
      return new ProxyFactory(advisedSupport).getProxy();
    }

    return bean;
  }

  @Override
  public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
    return pvs;
  }

  private boolean isInfrastructureClass(Class<?> beanClass) {
    return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
  }

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (isInfrastructureClass(bean.getClass())){
      return bean;
    }
    Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory
            .getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
    for (AspectJExpressionPointcutAdvisor advisor : advisors){
      ClassFilter classFilter = advisor.getPointcut().getClassFilter();
      if (!classFilter.matches(bean.getClass())){
        continue;
      }
      AdvisedSupport advisedSupport = new AdvisedSupport();
      TargetSource targetSource = new TargetSource(bean);
      advisedSupport.setTargetSource(targetSource);
      advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
      advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
      advisedSupport.setProxyTargetClass(false);

      // 返回代理对象
      return new ProxyFactory(advisedSupport).getProxy();
    }
    return bean;
  }
}
